#include "cpu.h"

/* CPU 初始值，程序寄存器PC的初始值默认为 0x3000 */
enum { 
    CPU_HALT = 0x0,
    CPU_BOOT = 0x1,
    PC_START = 0x3000
};

/* 陷入指令号 */
enum {
    TRAP_GETC  = 0x20,  // 从键盘读入一个字符到R0寄存器（阻塞，不回显）
    TRAP_OUT   = 0x21,  // 从R0寄存器中输出一个字符到终端
    TRAP_PUTS  = 0x22,  // 输出以R0寄存器中的内容为起始地址的字符串，直到遇见\0
    TRAP_IN    = 0x23,  // 从键盘读入一个字符到R0寄存器（阻塞，回显）
    TRAP_PUTSP = 0x24,  // 输出一个字节串
    TRAP_HALT  = 0x25   // 终止程序
};

/* 操作码 */
enum {
    OP_BR = 0, // 分支
    OP_ADD,    // 加法
    OP_LD,     // 加载，直接寻址
    OP_ST,     // 存回，间接寻址
    OP_JSR,    // 寄存器跳转
    OP_AND,    // 逐位与
    OP_LDR,    // 加载，基址+偏移
    OP_STR,    // 存回，基址+偏移
    OP_RTI,    // 保留，未使用
    OP_NOT,    // 逐位非
    OP_LDI,    // 加载，间接寻址
    OP_STI,    // 存回，间接寻址
    OP_JMP,    // 跳转
    OP_RES,    // 保留，未使用
    OP_LEA,    // 加载有效地址
    OP_TRAP    // 陷入
};

/* 条件标志位 */
enum {
    FL_POS = 1 << 0,    // P 正
    FL_ZRO = 1 << 1,    // Z 零
    FL_NEG = 1 << 2,    // N 负
};

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

#define CPU_TEMPLATE    template<                                \
                            typename word_t,    /* 机器字长 */    \
                            typename arch_t     /* 指令集架构 */  \
                        >

#define CPU_CLASS       CPU<word_t, arch_t>  

CPU_TEMPLATE
int CPU_CLASS::state()
{ 
    return status;
};

/**
 * @brief : 
 *      CPU 初始化
 */ 
CPU_TEMPLATE
void CPU_CLASS::init( )
{
    status = CPU_BOOT;
    reg[R_PC]   = PC_START;
    reg[R_FLAG] = FL_ZRO;
}

/**
 * @brief : 
 *      更新程序状态字
 * @param : 
 *      word_t r : 传入上一步运算的结果寄存器ming
 */
CPU_TEMPLATE
void CPU_CLASS::update_flags(word_t r) {
    if( 0==reg[r] ) {
        reg[R_FLAG] = FL_ZRO;
    }else if( reg[r] >> 15 ) {
        reg[R_FLAG] = FL_NEG;
    } else {
        reg[R_FLAG] = FL_POS;
    }
}

/**
 * @brief : 
 *      将一个数做符号位扩展,非负数按 0 扩展，负数按 1 扩展
 *      即非负数直接返回，负数用符号位扩展
 * @param : 
 *      word_t data : 待扩展的数
 *      word_t n : 待扩展的数data原本所占的位数
 * @return : 
 *      word_t : 扩展后的 16 位数据
 */
CPU_TEMPLATE
word_t CPU_CLASS::sign_extend(word_t data, word_t n)
{
    word_t sign = ( data >> (n-1) ) & 0x1;      // 最高位即为符号位
    return sign ? data | (0xFFFF << n) : data;  // 负数按符号位扩展，非负数直接返回
}

/**
 * @brief : 
 *      取指
 * @return : 
 *      arch_t : 返回一个指令对象
 */ 
CPU_TEMPLATE
void CPU_CLASS::fetch()
{
    word_t addr = reg[R_PC];    // 取指 PC 寄存器的值
    inst = load(addr);      // 根据地址从内存中读取指令
    reg[R_PC]++;                // PC 自增
}

/**
 * @brief : 
 *      译码
 * @param : 
 *      arch_t : 传入一个指令对象的引用
 */ 
CPU_TEMPLATE
void CPU_CLASS::decode()
{
    /* 先从指令中分离出各组成成分 */
    op = rd = rs = rt = cond = imm = 0;
    inst.part(op, rd, rs, rt);

    /* 根据指令类别设置条件标志 */
    cond = (inst >> 5) & 0x1;
    cond = OP_BR==op   ? rd & reg[R_FLAG]   : cond;
    cond = OP_JSR==op  ? (inst >> 11) & 0x1 : cond;
    cond = OP_TRAP==op ? inst & 0xff        : cond; 

    /* 再根据操作码做设置偏移值 */
    switch(op)
    {
        case OP_ADD:
        case OP_AND:
            imm = sign_extend(inst & 0x1f, 5);
            break;
        case OP_LDR:
        case OP_STR:
            imm = sign_extend(inst & 0x3f, 6);
            break;
        case OP_BR:
        case OP_LD:
        case OP_LDI:
        case OP_LEA:
        case OP_ST:
        case OP_STI:
            imm = sign_extend(inst & 0x1ff, 9);
            break;
        case OP_JSR:
            imm = sign_extend(inst & 0x7ff, 11);
            break;
    }
}

/**
 * @brief : 
 *      模拟CPU执行指令
 */
CPU_TEMPLATE
void CPU_CLASS::run()
{
    switch(op)
    {
        case OP_ADD:
            reg[rd] = cond ? reg[rs] + imm : reg[rs] + reg[rt];
            update_flags(rd);
            break;
        case OP_AND:
            reg[rd] = cond ? reg[rs] & imm : reg[rs] & reg[rt];
            update_flags(rd);
            break;
        case OP_NOT:
            reg[rd] = ~reg[rs];
            update_flags(rd);
            break;
        case OP_BR:
            reg[R_PC] = cond ? reg[R_PC] + imm : reg[R_PC];
            break;
        case OP_JMP:
            reg[R_PC] = reg[rs];
            break;
        case OP_JSR:
            reg[R_R7] = reg[R_PC];
            reg[R_PC] = cond ? reg[R_PC] + imm : reg[rs];
            break;
        case OP_LD:
            reg[rd] = load(reg[R_PC] + imm);
            update_flags(rd);
            break;
        case OP_LDI:
            reg[rd] = load( load(reg[R_PC] + imm) );
            update_flags(rd);
            break;
        case OP_LDR:
            reg[rd] = load(reg[rs] + imm);
            update_flags(rd);
            break;
        case OP_LEA:
            reg[rd] = reg[R_PC] + imm;
            update_flags(rd);
            break;
        case OP_ST:
            store(reg[R_PC] + imm, reg[rd]);
            break;
        case OP_STI:
            store( load(reg[R_PC] + imm), reg[rd] );
            break;
        case OP_STR:
            store(reg[rs] + imm, reg[rd]);
            break;
        case OP_TRAP:
            trap(cond);
            break;
        case OP_RES:
        case OP_RTI:
        default: 
            abort();
            break;
    }
}

/**
 * @brief : 
 *      系统陷阱指令
 * @param : 
 *      word_t : 传入一个陷入指令号
 */ 
CPU_TEMPLATE
void CPU_CLASS::trap(word_t id)
{
    switch(id) 
    {
        case TRAP_GETC: 
        {
            /* 从键盘读入一个字符到R0寄存器（阻塞，不回显）*/
            reg[R_R0] = (word_t)inchar();  
            update_flags(R_R0);
            break;
        }
        case TRAP_OUT:
        { 
            /* 从R0寄存器中输出一个字符到终端 */
            outc( (char)reg[R_R0] );
            flush();
            break;
        }
        case TRAP_PUTS: 
        { 
            /* 输出以R0寄存器中的内容为起始地址的字符串，直到遇见\0, 每字代表一个字符 */
            word_t addr = reg[R_R0];
            word_t data = load(addr);
            while(data) {
                outc( (char)data );
                addr++;
                data = load(addr);
            }
            flush();
            break;
        }
        case TRAP_IN: 
        {
            /* 从键盘读入一个字符到R0寄存器（阻塞，回显）*/
            outs("Enter a character: ");
            char ch = inchar();
            outc(ch);
            flush();
            reg[R_R0] = (word_t)ch;
            update_flags(R_R0);
            break; 
        } 
        case TRAP_PUTSP: 
        { 
            /* 输出字节串，每字节一个字符，先输出低字节 */
            word_t addr = reg[R_R0];
            word_t data = load(addr);
            while(data) {
                outc( (char)(data & 0xFF) );
                outc( (char)(data >> 8) );
                addr++;
                data = load(addr);
            }
            flush();
            break;
        }
        case TRAP_HALT: 
        {
            outs("HALT");
            flush();
            status = CPU_HALT;
            break;
        }
    } // end switch
}